{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6e6a0a39-9a4c-47ae-a238-1a3a847eea5b",
   "metadata": {},
   "source": [
    "# How to add runtime configuration to your graph\n",
    "\n",
    "Sometimes you want to be able to configure your agent when calling it. \n",
    "Examples of this include configuring which LLM to use.\n",
    "Below we walk through an example of doing so."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df1ff9cf-f8d2-4109-adf9-2adec83f5a95",
   "metadata": {},
   "source": [
    "## Base\n",
    "\n",
    "First, let's create a very simple graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "816523d0-0b59-47cf-9f4c-4838024efe22",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "from typing import Annotated, Sequence, TypedDict\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.messages import BaseMessage, HumanMessage\n",
    "\n",
    "from langgraph.graph import END, StateGraph\n",
    "\n",
    "model = ChatAnthropic(model_name=\"claude-2.1\")\n",
    "\n",
    "\n",
    "class AgentState(TypedDict):\n",
    "    messages: Annotated[Sequence[BaseMessage], operator.add]\n",
    "\n",
    "\n",
    "def _call_model(state):\n",
    "    response = model.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(AgentState)\n",
    "workflow.add_node(\"model\", _call_model)\n",
    "workflow.set_entry_point(\"model\")\n",
    "workflow.add_edge(\"model\", END)\n",
    "\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "070f11a6-2441-4db5-9df6-e318f110e281",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='hi'),\n",
       "  AIMessage(content='Hello!', response_metadata={'id': 'msg_01YZj7CVCUSc76faX4VM9i5d', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-d343db34-598c-46a2-93d6-ffa886d9b264-0')]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app.invoke({\"messages\": [HumanMessage(content=\"hi\")]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69a1dd47-c5b3-4e04-af56-45682f74d61f",
   "metadata": {},
   "source": [
    "## Configure the graph\n",
    "\n",
    "Great! Now let's suppose that we want to extend this example so the user is able to choose from multiple llms.\n",
    "We can easily do that by passing in a config.\n",
    "This config is meant to contain things are not part of the input (and therefore that we don't want to track as part of the state)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "c01f1e7c-8e8b-4e26-98f7-56ac225077b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "openai_model = ChatOpenAI()\n",
    "\n",
    "models = {\n",
    "    \"anthropic\": model,\n",
    "    \"openai\": openai_model,\n",
    "}\n",
    "\n",
    "\n",
    "def _call_model(state, config):\n",
    "    m = models[config[\"configurable\"].get(\"model\", \"anthropic\")]\n",
    "    response = m.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(AgentState)\n",
    "workflow.add_node(\"model\", _call_model)\n",
    "workflow.set_entry_point(\"model\")\n",
    "workflow.add_edge(\"model\", END)\n",
    "\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7741b75c-55ba-4c78-bbb1-5dc20a210f11",
   "metadata": {},
   "source": [
    "If we call it with no configuration, it will use the default as we defined it (Anthropic)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ef50f048-fc43-40c0-b713-346408fcf052",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='hi'),\n",
       "  AIMessage(content='Hello!', response_metadata={'id': 'msg_01EedReFyXmonWXPKhYre7Jb', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-1c6feaa0-bd6f-433a-8264-209d72c85db7-0')]}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app.invoke({\"messages\": [HumanMessage(content=\"hi\")]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6896b32-9b25-4342-bfd0-29a3d329a06a",
   "metadata": {},
   "source": [
    "We can also call it with a config to get it to use a different model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f2f7c74b-9fb0-41c6-9728-dcf9d8a3c397",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='hi'),\n",
       "  AIMessage(content='Hello! How can I assist you today?', response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 8, 'total_tokens': 17}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_3b956da36b', 'finish_reason': 'stop', 'logprobs': None}, id='run-d41ffb62-e164-45a1-862c-d288c6ad100a-0')]}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "config = {\"configurable\": {\"model\": \"openai\"}}\n",
    "app.invoke({\"messages\": [HumanMessage(content=\"hi\")]}, config=config)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4c7eaf1-4ee0-42b3-971d-273a108f205f",
   "metadata": {},
   "source": [
    "We can also adapt our graph to take in more configuration! Like a system message for example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f0393a43-9fbe-4056-972f-3e91ea329041",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage\n",
    "\n",
    "\n",
    "def _call_model(state, config):\n",
    "    m = models[config[\"configurable\"].get(\"model\", \"anthropic\")]\n",
    "    messages = state[\"messages\"]\n",
    "    if \"system_message\" in config[\"configurable\"]:\n",
    "        messages = [\n",
    "            SystemMessage(content=config[\"configurable\"][\"system_message\"])\n",
    "        ] + messages\n",
    "    response = m.invoke(messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(AgentState)\n",
    "workflow.add_node(\"model\", _call_model)\n",
    "workflow.set_entry_point(\"model\")\n",
    "workflow.add_edge(\"model\", END)\n",
    "\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "718685f7-4cdd-4181-9fc8-e7762d584727",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='hi'),\n",
       "  AIMessage(content='Hello!', response_metadata={'id': 'msg_01Ts56eVLSrUbzVMbzLnXc3M', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-f75a4389-b72e-4d47-8f3e-bedc6a060f66-0')]}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app.invoke({\"messages\": [HumanMessage(content=\"hi\")]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "e043a719-f197-46ef-9d45-84740a39aeb0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='hi'),\n",
       "  AIMessage(content='Ciao!', response_metadata={'id': 'msg_01RzFCii8WhbbkFm16nUquxk', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 14, 'output_tokens': 7}}, id='run-9492f0e4-f223-41c2-81a6-6f0cb6a14fe6-0')]}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "config = {\"configurable\": {\"system_message\": \"respond in italian\"}}\n",
    "app.invoke({\"messages\": [HumanMessage(content=\"hi\")]}, config=config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5c5f7f4-4b0e-4cde-93a6-c1c6329b8591",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
